In [ ]:
epochs = 10
n_test_batches = 200

Parte 11 - Classificação Segura Usando Aprendizagem Profunda

Os seus dados são importantes, o seu modelo também.

Os dados são o carro-chefe por trás da Aprendizagem de Máquina (Machine Learning). As organizações que criam e coletam dados são capazes de construir e treinar seus próprios modelos de aprendizagem de máquina. Isto permite-lhes oferecer o uso de tais modelos como um serviço (MLaaS) a organizações externas. Isto é útil para organizações que podem não ser capazes de criar esses modelos por si próprias, mas que ainda gostariam de usar esses modelos para fazer predições sobre os seus próprios dados.

No entanto, um modelo hospedado na nuvem ainda apresenta um problema de privacidade/IP. Para que organizações externas possam usá-lo - elas devem carregar seus dados de entrada (como imagens a serem classificadas) ou fazer o download do modelo. O upload dos dados de entrada pode ser problemático do ponto de vista da privacidade, mas o download do modelo pode não ser uma opção se a organização que criou/possui o modelo estiver preocupada em preservar seu IP.

Computação sobre dados criptografados

Neste contexto, uma possível solução seria criptografar tanto o modelo como os dados de uma forma que permita a uma organização utilizar um modelo de outra organização sem revelar o seu IP. Existem várias estratégias de criptografia que permitem o cálculo sobre dados criptografados, entre os quais Secure Multi-Party Computation (SMPC), criptografia Homomórfica (FHE/SHE) e criptografia Funcional (FE) são os tipos mais conhecidos. Vamos focar aqui na Computação Multiparte Segura (introduzida em detalhe aqui no tutorial 5) que consiste na soma de partes compartilhadas de forma privada. E que se baseia em protocolos de criptografia como o SecureNN e SPDZ, cujos detalhes são dados nesta excelente publicação de de blog.

Estes protocolos alcançam desempenhos notáveis sobre dados criptografados, e nos últimos meses temos trabalhado para tornar estes protocolos fáceis de usar. Em específico, estamos construindo ferramentas para permitir que você use esses protocolos sem ter que reimplementá-los novamente (ou se quer conhecer a criptografia por trás de seu funcionamento). Vamos direto ao assunto.

Cenário

Neste tutorial considere o seguinte cenário: considere que você é o servidor e que você tem alguns dados. Primeiro, você define e treina um modelo com esses dados de treinamento privado. Depois, você entra em contato com um cliente que possui alguns dos seus próprios dados e que gostaria de acessar o seu modelo para fazer algumas previsões/predições.

Você criptografa o seu modelo (uma rede neural). O cliente criptografa os seus dados. Ambos usam esses dois recursos criptografados para usar o modelo para classificar os dados. Finalmente, o resultado da predição é enviado de volta para o cliente de forma criptografada para que o servidor (ou seja, você) não aprenda nada sobre os dados do cliente (ou seja, você não aprende nem as entradas nem as predições).

O ideal seria que, de maneira incremental, compartilhássemos as entradas dos clientes entre eles próprios e o servidor e vice-versa para o modelo. Por uma questão de simplicidade, as entradas (i.e dados de treino) serão mantidas por outros dois workers , alice e bob. Se você considerar que alice é de propriedade do cliente e bob do servidor, é completamente equivalente.

O cálculo é seguro no modelo HBC (i.e. Honest-But-Curious adversary), que é padrão em muitas ferramentas MPC (Multi-Party Computation).

Agora temos tudo o que precisamos!

Autor:

Tradução:

Vamos começar!

Imports e especificações do modelo


In [ ]:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms

Nós também precisamos executar comandos específicos para importar/iniciar o PySyft. Nós criamos alguns workers (chamados client, bob e alice). Por fim, nós definimos o provedor de criptografia (cripto_provider) que nos dá todos as ferramentas básicas de criptografia que podemos precisar (Veja nosso tutorial sobre SMPC para mais detalhes).


In [ ]:
import syft as sy
hook = sy.TorchHook(torch) 
client = sy.VirtualWorker(hook, id="client")
bob = sy.VirtualWorker(hook, id="bob")
alice = sy.VirtualWorker(hook, id="alice")
crypto_provider = sy.VirtualWorker(hook, id="crypto_provider")

Nós definimos o cenário da tarefa de aprendizagem


In [ ]:
class Arguments():
    def __init__(self):
        self.batch_size = 64
        self.test_batch_size = 50
        self.epochs = epochs
        self.lr = 0.001
        self.log_interval = 100

args = Arguments()

Carregamento e envio de dados aos Workers

Na nosso cenário, assumimos que o servidor tem acesso a alguns dados de treino para que possa treinar seu modelo. Aqui temos o conjunto de treinamento do MNIST.


In [ ]:
train_loader = torch.utils.data.DataLoader(
    datasets.MNIST('../data', train=True, download=True,
                   transform=transforms.Compose([
                       transforms.ToTensor(),
                       transforms.Normalize((0.1307,), (0.3081,))
                   ])),
    batch_size=args.batch_size, shuffle=True)

Em segundo lugar, o cliente tem alguns dados e gostaria de ter predições sobre eles usando o modelo do servidor. Este cliente criptografa seus dados, compartilhando-os entre os dois workers , alice e bob.

SMPC usa protocolos de criptografia que requerem o uso de inteiros. Nós aproveitamos aqui a abstração do tensor PySyft para converter os tensores de ponto flutuante do PyTorch em tensores de precisão fixa utilizando .fix_precision(). Por exemplo 0.123 com precisão 2 faz um arredondamento com o segundo dígito decimal para que o número armazenado seja o número inteiro 12.


In [ ]:
test_loader = torch.utils.data.DataLoader(
    datasets.MNIST('../data', train=False,
                   transform=transforms.Compose([
                       transforms.ToTensor(),
                       transforms.Normalize((0.1307,), (0.3081,))
                   ])),
    batch_size=args.test_batch_size, shuffle=True)

private_test_loader = []
for data, target in test_loader:
    private_test_loader.append((
        data.fix_precision().share(alice, bob, crypto_provider=crypto_provider),
        target.fix_precision().share(alice, bob, crypto_provider=crypto_provider)
    ))

Especificação de uma Rede Neural (Feed Forward Neural Network)

Aqui está a especificação da rede utilizada pelo servidor


In [ ]:
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(784, 500)
        self.fc2 = nn.Linear(500, 10)

    def forward(self, x):
        x = x.view(-1, 784)
        x = self.fc1(x)
        x = F.relu(x)
        x = self.fc2(x)
        return x

Iniciar o treinamento

O treinamento é feito localmente, então este é um treinamento no PyTorch inteiramente local, nada de especial aqui!


In [ ]:
def train(args, model, train_loader, optimizer, epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        optimizer.zero_grad()
        output = model(data)
        output = F.log_softmax(output, dim=1)
        loss = F.nll_loss(output, target)
        loss.backward()
        optimizer.step()
        if batch_idx % args.log_interval == 0:
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * args.batch_size, len(train_loader) * args.batch_size,
                100. * batch_idx / len(train_loader), loss.item()))

In [ ]:
model = Net()
optimizer = torch.optim.Adam(model.parameters(), lr=args.lr)

for epoch in range(1, args.epochs + 1):
    train(args, model, train_loader, optimizer, epoch)

In [ ]:
def test(args, model, test_loader):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            output = model(data)
            output = F.log_softmax(output, dim=1)
            test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
            pred = output.argmax(1, keepdim=True) # get the index of the max log-probability 
            correct += pred.eq(target.view_as(pred)).sum().item()

    test_loss /= len(test_loader.dataset)

    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))

In [ ]:
test(args, model, test_loader)

O nosso modelo está agora treinado e pronto para ser fornecido como um serviço!

Avaliação segura

Agora, como servidor, enviamos o modelo para os workers que têm os dados. Como o modelo é informação sensível (você gastou tempo otimizando-o!), você não quer revelar seus pesos então você compartilha o modelo, de forma secreta/criptografada, como nós fizemos com o conjunto de dados anteriormente.


In [ ]:
model.fix_precision().share(alice, bob, crypto_provider=crypto_provider)

A seguinte função de teste realiza uma avaliação criptografada. Os pesos do modelo, as entradas de dados, a predição e o alvo (target) utilizado para a pontuação são criptografados!

No entanto, a sintaxe é muito semelhante ao teste de modelos usual do PyTorch, não é legal/Fixe?!

A única coisa que deciframos/descriptografamos no lado do servidor é a pontuação final dos nossos 200 batches de itens (n_test_batches) para verificar se as previsões eram em média boas.


In [ ]:
def test(args, model, test_loader):
    model.eval()
    n_correct_priv = 0
    n_total = 0
    with torch.no_grad():
        for data, target in test_loader[:n_test_batches]:
            output = model(data)
            pred = output.argmax(dim=1) 
            n_correct_priv += pred.eq(target.view_as(pred)).sum()
            n_total += args.test_batch_size

            n_correct = n_correct_priv.copy().get().float_precision().long().item()
    
            print('Test set: Accuracy: {}/{} ({:.0f}%)'.format(
                n_correct, n_total,
                100. * n_correct / n_total))

In [ ]:
test(args, model, private_test_loader)

Et voilà! Aqui está, você aprendeu como fazer previsões/predições seguras de ponta a ponta: os pesos do modelo do servidor não vazaram para o cliente e o servidor não tem informações sobre os dados de entrada nem sobre a saída da classificação!

Quanto ao desempenho, classificar uma imagem leva menos de 0,1 segundo, aproximadamente 33ms no meu computador portátil (2,7 GHz Intel Core i7, 16GB RAM). Entretanto, utilizei uma comunicação muito rápida (todos os workers estão na minha máquina local). O desempenho irá variar dependendo da rapidez com que os diferentes workers podem se comunicar uns com os outros.

Conclusão

Você já viu como é fácil usar o PyTorch e o PySyft para realizar Aprendizagem de Máquina segura (i.e Secure Machine Learning) na prática e proteger os dados dos usuários, sem ter que ser um especialista em criptografia!

Mais informações sobre este tópico virão em breve, incluindo camadas convolucionais para avaliar adequadamente o desempenho do PySyft em relação a outros casos de uso, bem como o treinamento criptografado privado de redes neurais, que é necessário quando uma organização recorre a dados sensíveis externos para treinar seu próprio modelo. Fique atento!

Se você gostou disso e gostaria de participar do movimento em prol da preservação da privacidade, da propriedade descentralizada da IA e da cadeia de fornecimento de IA (dados), você pode fazer isso das seguintes maneiras!

Dê-nos uma estrela em nosso repo do PySyft no GitHub

A maneira mais fácil de ajudar nossa comunidade é adicionando uma estrela nos nossos repositórios! Isso ajuda a aumentar a conscientização sobre essas ferramentas legais que estamos construindo.

Veja nossos tutoriais no GitHub!

Fizemos tutoriais muito bons para entender melhor como deve ser a Aprendizagem Federada e a proteção de Privacidade, e como estamos construindo as coisas básicas que precisamos para fazer com que isso aconteça.

Junte-se ao Slack!

A melhor maneira de manter-se atualizado sobre os últimos avanços é se juntar à nossa comunidade!

Contribua com o projeto!

A melhor maneira de contribuir para a nossa comunidade é se tornando um contribuidor do código! A qualquer momento, você pode acessar a página de Issues (problemas) do PySyft no GitHub e filtrar por "Projetos". Isso mostrará todas as etiquetas (tags) na parte superior, com uma visão geral de quais projetos você pode participar! Se você não deseja ingressar em um projeto, mas gostaria de codificar um pouco, também pode procurar mais mini-projetos "independentes" pesquisando problemas no GitHub marcados como "good first issue".

Doar

Se você não tem tempo para contribuir com nossa base de códigos, mas ainda deseja nos apoiar, também pode se tornar um Apoiador em nosso Open Collective. Todas as doações vão para hospedagem na web e outras despesas da comunidade, como hackathons e meetups!

Página do Open Collective do OpenMined


In [ ]: